<template>
  <div class="draggable-tree-container">
    <!-- 调试信息 -->
    <div class="debug-panel">
      <div>
        <strong>拖拽状态:</strong> {{ isDragging ? "正在拖拽" : "未拖拽" }}
      </div>
      <div v-if="dragNode && dragNode.key">
        <strong>当前拖拽节点:</strong> {{ dragNode.key }}
      </div>
      <div v-if="currentTargetKey">
        <strong>当前目标节点:</strong> {{ currentTargetKey }}
      </div>
      <div v-if="dropPosition !== null">
        <strong>放置位置:</strong> {{ dropPosition }}
      </div>
    </div>

    <!-- 错误提示 -->
    <div v-if="errorMessage" class="error-message">
      <div class="error-content">
        <span>{{ errorMessage }}</span>
        <a-button type="primary" size="small" @click="clearError"
          >关闭</a-button
        >
      </div>
    </div>

    <a-spin :spinning="loading">
      <a-tree
      :show-line="true"
        class="draggable-tree"
        :tree-data="treeData"
        :draggable="{ icon: false, nodeDraggable: () => true }"
        :blockNode="true"
        @drop="onDrop"
        @dragenter="onDragEnter"
        @dragleave="onDragLeave"
        @dragover="onDragOver"
        @dragstart="onDragStart"
      >
        <template #title="{ title, key }">
          <div
            class="custom-node"
            :class="{
              'is-dragging': isDragging && dragNode && dragNode.key === key,
            }"
            @mouseover="logNodeInfo($event, title, key)"
          >
            <span>{{ title }}</span>
          </div>
        </template>
      </a-tree>
    </a-spin>

    <!-- 全局提示框 - 使用绝对定位与可控制的位置 -->
    <div v-if="isDragging && dragNode" 
         class="global-tip-container"
         :style="{
           left: tipPosition.left + 'px',
           top: tipPosition.top + 'px'
         }">
      <!-- 不允许跨级移动提示 -->
      <div v-if="currentTargetKey && dropPosition === 'invalid'" class="tip-box invalid-tip">
        不能跨一级以上移动
      </div>
      
      <!-- 正常拖拽提示 -->
      <div v-if="currentTargetKey && dropPosition === -1" class="tip-box normal-tip">
        将放置在【{{ getNodeTitle(currentTargetKey) }}】的上方
      </div>
      <div v-if="currentTargetKey && dropPosition === 0" class="tip-box normal-tip">
        将与【{{ getNodeTitle(currentTargetKey) }}】合并
      </div>
      <div v-if="currentTargetKey && dropPosition > 0" class="tip-box normal-tip">
        将放置在【{{ getNodeTitle(currentTargetKey) }}】的下方
      </div>
    </div>

    <!-- 保存按钮 -->
    <div class="action-bar">
      <a-button type="primary" @click="saveTreeData" :loading="loading"
        >保存变更</a-button
      >
    </div>
  </div>
</template>

<script setup>
import { ref, reactive, watch, onMounted, onUnmounted, nextTick } from "vue";
import { getTreeData, updateTreeData } from "../mock/index.js";
import lodash from "lodash";

// 树数据
const treeData = ref([]);
// 加载状态
const loading = ref(false);
// 拖拽状态
const dragNode = ref(null);
const dropPosition = ref(null);
const currentTargetKey = ref(null);
const isDragging = ref(false);
const dragTargetInfo = ref(null);
const errorMessage = ref("");
const lastCalculatedPosition = ref(null);
const tipPosition = ref({ left: 0, top: 0 });

// 初始化树数据
const initTreeData = async () => {
  loading.value = true;
  try {
    const data = await getTreeData();
    treeData.value = data;
  } catch (error) {
    errorMessage.value = "获取数据失败！";
  } finally {
    loading.value = false;
  }
};

// 保存树数据
const saveTreeData = async () => {
  loading.value = true;
  try {
    const result = await updateTreeData(treeData.value);
    if (!result.success) {
      errorMessage.value = "保存数据失败！";
    }
  } catch (error) {
    errorMessage.value = "保存数据失败！";
  } finally {
    loading.value = false;
  }
};

// 记录节点信息用于调试
const logNodeInfo = () => {};

// 全局错误处理
const handleError = (error) => {
  errorMessage.value = "拖拽操作出错，请重试";
  isDragging.value = false;
  dragTargetInfo.value = null;
  // 自动清除错误信息
  setTimeout(() => {
    errorMessage.value = "";
  }, 3000);
};

// 清除错误信息
const clearError = () => {
  errorMessage.value = "";
};

// 组件加载时获取数据
onMounted(() => {
  initTreeData();
  window.addEventListener("error", handleError);
});

onUnmounted(() => {
  window.removeEventListener("error", handleError);
});

// 安全地执行函数，添加错误处理
const safeExecute = (fn) => {
  return (...args) => {
    try {
      return fn(...args);
    } catch (error) {
      handleError(error);
      return null;
    }
  };
};

// 获取节点信息，包括标题
const getNodeInfo = safeExecute((key) => {
  if (!key) return null;

  const findNode = (nodes, targetKey) => {
    for (let i = 0; i < nodes.length; i++) {
      const node = nodes[i];
      if (node.key === targetKey) {
        return node;
      }
      if (node.children) {
        const found = findNode(node.children, targetKey);
        if (found) {
          return found;
        }
      }
    }
    return null;
  };

  return findNode(treeData.value, key);
});

// 获取节点的层级路径
const getNodePath = safeExecute((key) => {
  const findPath = (nodes, targetKey, currentPath = []) => {
    for (let i = 0; i < nodes.length; i++) {
      const node = nodes[i];
      const newPath = [...currentPath, node];

      if (node.key === targetKey) {
        return newPath;
      }

      if (node.children) {
        const result = findPath(node.children, targetKey, newPath);
        if (result) {
          return result;
        }
      }
    }
    return null;
  };

  return findPath(treeData.value, key);
});

// 获取父节点下所有子节点，并按顺序排列（用于恢复顺序）
const getAllSiblings = safeExecute((parentNode) => {
  if (!parentNode || !parentNode.children) return [];
  return [...parentNode.children];
});

// 获取节点的父节点Key
const getNodeParentKey = safeExecute((key) => {
  if (!key) return null;

  // 先获取节点路径
  const path = getNodePath(key);
  if (!path || path.length <= 1) return null; // 顶级节点没有父节点

  // 返回父节点的key
  return path[path.length - 2].key;
});

// 判断是否是子节点
const isChildNode = safeExecute((parentKey, childKey) => {
  if (!parentKey || !childKey) return false;
  
  const childPath = getNodePath(childKey);
  if (!childPath) return false;
  
  return childPath.some(node => node.key === parentKey);
});

// 拖拽开始时记录被拖拽的节点
const onDragStart = safeExecute((e) => {
  // 检查事件对象是否包含必要的属性
  if (!e || !e.node || !e.node.key) {
    return;
  }

  // 保存原始节点对象的关键数据，避免直接使用Proxy
  dragNode.value = {
    key: e.node.key,
    title: e.node.title,
    dataRef: e.node.dataRef || {},
  };

  isDragging.value = true;

  // 将当前拖拽的节点键设置为目标键，以显示拖拽提示
  currentTargetKey.value = e.node.key;

  // 添加一个全局class，用于控制拖拽过程中的提示框位置
  document.body.classList.add("dragging-active");

  // 如果有鼠标位置信息，初始化提示框位置
  if (e.event) {
    tipPosition.value = {
      left: e.event.clientX - 100,
      top: e.event.clientY + 30
    };
  }
});

// 计算拖拽位置
const calculateDropPosition = (mouseY, nodeElement) => {
  if (!nodeElement) {
    return 0; // 默认中间位置
  }
  
  // 获取节点的几何信息
  const rect = nodeElement.getBoundingClientRect();
  const offsetY = mouseY - rect.top;
  const height = rect.height;

  // 根据鼠标位置计算拖拽位置
  if (offsetY < height * 0.33) {
    return -1; // 上部区域 - 放在节点前面
  } else if (offsetY < height * 0.66) {
    return 0;  // 中间区域 - 合并操作
  } else {
    return 1;  // 下部区域 - 放在节点后面
  }
};

// 判断节点间的级别差异（新添加的工具函数）
const getLevelDifference = safeExecute((dragPath, dropPath) => {
  if (!dragPath || !dropPath) return Infinity;
  
  // 获取路径长度（即节点深度）
  const dragLevel = dragPath.length;
  const dropLevel = dropPath.length;
  
  // 计算深度差异的绝对值
  return Math.abs(dragLevel - dropLevel);
});

// 拖拽进入目标节点
const onDragEnter = safeExecute((e) => {
  if (!e || !e.event || !e.node || !e.node.key) return;

  e.event.preventDefault();

  // 目标节点信息
  const targetKey = e.node.key;

  // 必须要有拖拽节点信息
  if (!dragNode.value || !dragNode.value.key) return;

  // 获取拖拽节点信息
  const dragKey = dragNode.value.key;
  
  // 如果拖拽到自己，不显示任何提示
  if (dragKey === targetKey) {
    dropPosition.value = null;
    currentTargetKey.value = null;
    lastCalculatedPosition.value = null;
    return;
  }

  // 获取节点路径
  const dragPath = getNodePath(dragKey);
  const dropPath = getNodePath(targetKey);

  if (!dragPath || !dropPath) {
    return;
  }

  // 检查是否拖拽到自己的子节点
  let isChildOfDrag = false;
  for (let i = 0; i < dropPath.length; i++) {
    if (dropPath[i].key === dragKey) {
      isChildOfDrag = true;
      break;
    }
  }

  if (isChildOfDrag) {
    // 不设置目标键和拖拽位置，这样就不会显示提示
    currentTargetKey.value = null;
    dropPosition.value = null;
    lastCalculatedPosition.value = null;
    return;
  }

  // 获取父节点信息
  const dragParentKey = getNodeParentKey(dragKey);
  const dropParentKey = getNodeParentKey(targetKey);
  
  // 计算层级差异
  const levelDifference = getLevelDifference(dragPath, dropPath);
  
  // 先判断是否为跨级移动（高优先级判断）
  const isSameLevel = dragParentKey === dropParentKey;
  const isChildToParent = dragParentKey === targetKey;
  const areBothRootNodes = dragParentKey === null && dropParentKey === null;
  
  if (!isSameLevel && !isChildToParent && !areBothRootNodes) {
    // 检查层级差异是否在允许范围内（不超过1级）
    if (levelDifference > 1) {
      // 设置特殊拖拽位置值'invalid'，表示不允许的操作
      dropPosition.value = 'invalid';
      lastCalculatedPosition.value = 'invalid';
      currentTargetKey.value = targetKey; // 保留目标键以显示提示
      return;
    }
    
    // 检查是否为特殊情况 - 上移到叔父节点（同一祖父节点下的节点之间的移动）
    let isSpecialCase = false;
    if (dragPath.length > 1 && dropPath.length > 1) {
      const dragGrandParent = dragPath.length > 1 ? dragPath[dragPath.length - 2] : null;
      const dropGrandParent = dropPath.length > 1 ? dropPath[dropPath.length - 2] : null;
      
      if (dragGrandParent && dropGrandParent && dragGrandParent.key === dropGrandParent.key) {
        isSpecialCase = true;
      }
    }
    
    if (!isSpecialCase && levelDifference > 1) {
      // 设置特殊拖拽位置值'invalid'，表示不允许的操作
      dropPosition.value = 'invalid';
      lastCalculatedPosition.value = 'invalid';
      currentTargetKey.value = targetKey; // 保留目标键以显示提示
      return;
    }
  }

  // 显示目标节点的提示
  currentTargetKey.value = targetKey;

  // 手动计算拖拽位置
  const mouseY = e.event.clientY;

  // 获取节点DOM元素
  const nodeElement = e.event.target.closest(".custom-node");
  const position = calculateDropPosition(mouseY, nodeElement);
  
  // 更新位置
  dropPosition.value = position;
  lastCalculatedPosition.value = position;
});

// 拖拽离开目标节点
const onDragLeave = safeExecute((e) => {
  dropPosition.value = null;
  currentTargetKey.value = null;
});

// 拖拽悬停 - 简化版，复用DragEnter的逻辑
const onDragOver = safeExecute((e) => {
  if (!e || !e.event || !e.node || !e.node.key) return;

  e.event.preventDefault();

  // 获取拖拽节点信息
  const dragKey = dragNode.value ? dragNode.value.key : null;
  const targetKey = e.node.key;
  
  // 如果拖拽到自己，不显示任何提示
  if (dragKey === targetKey) {
    currentTargetKey.value = null;
    dropPosition.value = null;
    lastCalculatedPosition.value = null;
    return;
  }

  // 如果目标节点已经变化，直接调用DragEnter方法处理
  if (currentTargetKey.value !== e.node.key) {
    onDragEnter(e);
    return;
  }

  // 更新提示框位置
  if (isDragging.value && e.event) {
    tipPosition.value = {
      left: e.event.clientX - 100,
      top: e.event.clientY + 30
    };
  }

  // 获取节点路径，用于跨级判断
  const dragPath = getNodePath(dragKey);
  const dropPath = getNodePath(targetKey);
  
  // 计算层级差异
  const levelDifference = getLevelDifference(dragPath, dropPath);

  // 获取父节点信息
  const dragParentKey = getNodeParentKey(dragKey);
  const dropParentKey = getNodeParentKey(targetKey);
  
  // 先判断是否为跨级移动（高优先级判断）
  const isSameLevel = dragParentKey === dropParentKey;
  const isChildToParent = dragParentKey === targetKey;
  const areBothRootNodes = dragParentKey === null && dropParentKey === null;
  
  if (!isSameLevel && !isChildToParent && !areBothRootNodes) {
    // 检查是否跨越超过一级
    if (levelDifference > 1) {
      // 设置为无效操作
      if (dropPosition.value !== 'invalid') {
        dropPosition.value = 'invalid';
        lastCalculatedPosition.value = 'invalid';
      }
      return;
    }
    
    // 检查是否为特殊情况
    let isSpecialCase = false;
    if (dragPath && dropPath && dragPath.length > 1 && dropPath.length > 1) {
      const dragGrandParent = dragPath[dragPath.length - 2];
      const dropGrandParent = dropPath[dropPath.length - 2];
      
      if (dragGrandParent && dropGrandParent && dragGrandParent.key === dropGrandParent.key) {
        isSpecialCase = true;
      }
    }
    
    if (!isSpecialCase && levelDifference > 1) {
      // 设置为无效操作
      if (dropPosition.value !== 'invalid') {
        dropPosition.value = 'invalid';
        lastCalculatedPosition.value = 'invalid';
      }
      return;
    }
  }

  // 否则只更新拖拽位置
  // 获取鼠标位置
  const mouseY = e.event.clientY;

  // 获取节点DOM元素
  const nodeElement = e.event.target.closest(".custom-node");
  
  // 计算新位置
  const newPosition = calculateDropPosition(mouseY, nodeElement);

  // 只有在位置变化时才更新
  if (newPosition !== dropPosition.value) {
    dropPosition.value = newPosition;
    lastCalculatedPosition.value = newPosition;
  }
});

// 查找节点和其父节点的键
const findNodeWithParentKey = (data, key, parentKey = null) => {
  for (let i = 0; i < data.length; i++) {
    if (data[i].key === key) {
      return { node: data[i], index: i, parentArr: data, parentKey };
    }
    if (data[i].children) {
      const result = findNodeWithParentKey(
        data[i].children,
        key,
        data[i].key
      );
      if (result.node) {
        return result;
      }
    }
  }
  return { node: null, index: -1, parentArr: null, parentKey: null };
};

// 拖拽完成处理
const onDrop = safeExecute((e) => {
  if (!e || !e.event) return;

  e.event.preventDefault();
  
  // 先清除拖拽状态，确保提示框隐藏
  const finalDropPosition = lastCalculatedPosition.value;
  currentTargetKey.value = null;
  dropPosition.value = null;
  lastCalculatedPosition.value = null;

  // 检查事件对象是否包含必要的属性
  if (!e.node || !e.node.key) {
    return;
  }

  // 获取必要的键
  const dropKey = e.node.key;
  const dragKey = dragNode.value ? dragNode.value.key : null;
  // 使用我们自己计算的位置，而不是Ant Design Tree提供的位置
  const dropPos = finalDropPosition !== null ? finalDropPosition : e.dropPosition;

  if (!dragKey || dragKey === dropKey) {
    return; // 不能拖拽到自己
  }

  // 获取父节点信息
  const dragParentKey = getNodeParentKey(dragKey);
  const dropParentKey = getNodeParentKey(dropKey);

  // 检查是否拖拽到子节点
  const dragPath = getNodePath(dragKey);
  const dropPath = getNodePath(dropKey);

  if (!dragPath || !dropPath) {
    return;
  }

  // 计算层级差异
  const levelDifference = getLevelDifference(dragPath, dropPath);

  // 检查是否拖拽到自己的子节点
  let isChildOfDrag = false;
  for (let i = 0; i < dropPath.length; i++) {
    if (dropPath[i].key === dragKey) {
      isChildOfDrag = true;
      break;
    }
  }

  if (isChildOfDrag) {
    errorMessage.value = "不允许拖拽到自己的子节点！";
    setTimeout(() => {
      errorMessage.value = "";
    }, 3000);
    return;
  }

  // 判断特殊情况：子节点拖放到父节点
  const isChildToParent = dragParentKey === dropKey;
  
  // 根据是否子节点拖放到父节点，决定操作类型
  let operationType = "";
  
  if (isChildToParent) {
    // 无论放置位置如何，都视为排序到首位
    operationType = "sort_to_first";
  } else {
    // 标准化放置位置
    const normalizedDropPos = dropPos === 0 ? 0 : (dropPos < 0 ? -1 : 1);
    
    // 其他情况根据放置位置判断
    if (normalizedDropPos === 0) {
      operationType = "merge";
    } else {
      operationType = "move";
    }
  }
  
  // 判断是否允许拖拽操作
  let allowDrop = false;
  
  // 情况1: 同级节点排序 或 都是根节点
  const areBothRootNodes = dragParentKey === null && dropParentKey === null;
  if ((dragParentKey === dropParentKey && dragParentKey !== null) || areBothRootNodes) {
    allowDrop = true;
  } 
  // 情况2: 合并操作 (成为子节点)
  else if (operationType === "merge" && !isChildOfDrag) {
    // 检查合并操作是否跨越超过一级
    allowDrop = levelDifference <= 1;
  }
  // 情况3: 子节点拖到父节点（排序到第一位）
  else if (isChildToParent) {
    allowDrop = true;
  }
  // 特殊情况4: 特殊排序 - 目标节点是拖拽节点当前父节点的兄弟节点
  else if (dragPath.length > 1 && dropPath.length > 1) {
    const dragGrandParent = dragPath[dragPath.length - 2];
    const dropGrandParent = dropPath[dropPath.length - 2];
    
    if (dragGrandParent && dropGrandParent && dragGrandParent.key === dropGrandParent.key) {
      allowDrop = true;
    } else {
      // 检查是否跨越超过一级
      allowDrop = levelDifference <= 1;
    }
  }
  
  if (!allowDrop) {
    errorMessage.value = "不能跨一级以上拖拽！";
    setTimeout(() => {
      errorMessage.value = "";
    }, 3000);
    return;
  }

  // 复制一份数据用于操作
  const newData = lodash.cloneDeep(treeData.value);

  // 查找拖拽节点
  const dragResult = findNodeWithParentKey(newData, dragKey);
  const dragObj = dragResult.node ? { ...dragResult.node } : null;
  const dragParentArr = dragResult.parentArr;
  const dragIndex = dragResult.index;

  if (!dragObj || !dragParentArr) {
    return;
  }

  // 查找目标节点
  const dropResult = findNodeWithParentKey(newData, dropKey);
  const dropObj = dropResult.node;
  const dropParentArr = dropResult.parentArr;
  const dropIndex = dropResult.index;

  if (!dropObj || !dropParentArr) {
    return;
  }

  // 根据操作类型执行不同的处理
  if (operationType === "merge") {
    // 保存拖拽节点的标题和子节点，用于合并
    const dragTitle = dragObj.title;
    const dropTitle = dropObj.title;
    const dragChildren = dragObj.children || [];

    // 合并节点名称 - 恢复这个功能
    dropObj.title = dropTitle + " + " + dragTitle;

    // 确保目标节点有children数组
    if (!dropObj.children) {
      dropObj.children = [];
    }

    // 将拖拽节点的子节点合并到目标节点
    if (dragChildren.length > 0) {
      dragChildren.forEach((child) => {
        dropObj.children.push(child);
      });
    }

    // 从原位置删除拖拽节点
    dragParentArr.splice(dragIndex, 1);
  } 
  else if (operationType === "sort_to_first") {
    // 先从原位置删除拖拽节点
    dragParentArr.splice(dragIndex, 1);
    
    // 确保目标节点有children数组
    if (!dropObj.children) {
      dropObj.children = [];
    }
    
    // 放在子节点列表的第一位
    dropObj.children.unshift(dragObj);
  }
  else {
    // 处理非合并操作 (dropPos 为 -1 或 1 或 2)
    
    // 先从原位置删除拖拽节点
    dragParentArr.splice(dragIndex, 1);

    // 上方或下方插入
    let insertIndex;
    if (dropPos < 0) {
      // 放在目标节点之前
      insertIndex = dropIndex;
    } else {
      // 放在目标节点之后 (处理可能的值 1 或 2 或更高)
      insertIndex = dropIndex + 1;
    }

    // 执行插入操作
    dropParentArr.splice(insertIndex, 0, dragObj);
  }

  // 更新树数据
  treeData.value = newData;

  // 清除拖拽状态
  currentTargetKey.value = null;
  dropPosition.value = null;
  isDragging.value = false;

  // 移除全局class
  document.body.classList.remove("dragging-active");
});

// 辅助函数，查找指定键的节点
const findNodeByKey = (data, key) => {
  for (let i = 0; i < data.length; i++) {
    const node = data[i];
    if (node.key === key) {
      return node;
    }
    if (node.children) {
      const found = findNodeByKey(node.children, key);
      if (found) {
        return found;
      }
    }
  }
  return null;
};

// 辅助函数，获取节点标题
const getNodeTitle = (key) => {
  if (!key) return "";
  
  const findNodeTitle = (nodes, targetKey) => {
    for (let i = 0; i < nodes.length; i++) {
      const node = nodes[i];
      if (node.key === targetKey) {
        return node.title;
      }
      if (node.children) {
        const title = findNodeTitle(node.children, targetKey);
        if (title) {
          return title;
        }
      }
    }
    return "";
  };
  
  return findNodeTitle(treeData.value, key);
};

// 导出组件属性和方法
defineExpose({
  treeData,
});
</script>

<style scoped>
.draggable-tree-container {
  width: 100%;
  height: 100%;
  padding: 20px;
  position: relative;
}

.draggable-tree {
  background-color: #fff;
  border-radius: 4px;
  font-size: 16px;
  margin-bottom: 20px;
}

/* 修复树图标垂直居中问题 */
:deep(.ant-tree-treenode) {
  display: flex;
  align-items: center;
  padding: 4px 0;
}

:deep(.ant-tree-switcher) {
  display: flex;
  align-items: center;
  justify-content: center;
}

:deep(.ant-tree-node-content-wrapper) {
  display: flex;
  align-items: center;
}

.custom-node {
  position: relative;
  padding: 10px 0;
  transition: all 0.3s;
  cursor: move;
  border: 1px solid transparent;
  z-index: 10;
  overflow: visible;
  display: flex;
  align-items: center;
}

.custom-node.is-dragging {
  opacity: 0.5;
  border: 1px dashed #4782f8;
}

/* 全局提示框样式 */
.global-tip-container {
  position: fixed;
  z-index: 9999;
  pointer-events: none;
  transition: transform 0.05s ease-out; /* 添加平滑过渡效果 */
}

.tip-box {
  background-color: #3ba2f0;
  color: white;
  padding: 8px 16px;
  border-radius: 4px;
  font-size: 14px;
  white-space: nowrap;
  font-weight: bold;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  opacity: 1 !important;
  visibility: visible !important;
  pointer-events: none;
}

.invalid-tip {
  background-color: #ff4d4f;
  border: 1px solid #ff7875;
}

.normal-tip {
  background-color: #3ba2f0;
}

/* 错误提示 */
.error-message {
  position: fixed;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 1001;
}

.error-content {
  background-color: #ff4d4f;
  color: white;
  padding: 8px 16px;
  border-radius: 4px;
  font-size: 14px;
  font-weight: bold;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 10px;
}

.error-content a-button {
  margin-left: 16px;
}

/* 调试面板 */
.debug-panel {
  background-color: #f5f5f5;
  padding: 10px;
  margin-bottom: 10px;
  border-radius: 4px;
  border: 1px solid #d9d9d9;
  font-size: 12px;
  min-height: 120px;
}

.debug-panel div {
  margin-bottom: 5px;
}

/* 操作栏 */
.action-bar {
  margin-top: 20px;
  display: flex;
  justify-content: flex-end;
}
</style>
